home *** CD-ROM | disk | FTP | other *** search
/ Mac Easy 2010 May / Mac Life Ubuntu.iso / casper / filesystem.squashfs / usr / share / pyshared / launchpadbugs / buglistbase.py < prev    next >
Encoding:
Python Source  |  2009-01-08  |  7.3 KB  |  207 lines

  1. """TODO:
  2.  * move urlcheck to subclasses"""
  3.  
  4. import copy
  5. import itertools
  6.  
  7. from exceptions import LaunchpadError
  8.  
  9.  
  10. class LPBugPage(object):
  11.     """ Base-class which represents one batch of bugs
  12.     
  13.     Each subclass must implement a staticmethod 'find_parse_function'
  14.     which delegates the actual parsing.
  15.     A LPBugPage-object is iterable and has aditional attributes
  16.       - .parser: generator over elements of the page, this generator
  17.           is also accesible directly via __iter__
  18.       - .following_page: False or url of next batch
  19.       - .batchsize: number of items on this bugpage
  20.       - .length: overall size of the search result
  21.     """
  22.     @staticmethod
  23.     def find_parse_function(connection, url, all_tasks):
  24.         raise NotImplementedError, 'this method must be implemented by a concrete subclass'
  25.     
  26.     def __init__(self, url, connection, all_tasks=False):
  27.         assert hasattr(connection, "get"), "Connection object needed"
  28.         
  29.         (self.parser, self.following_page,
  30.          self.batchsize, self.length) = self.find_parse_function(connection, url, all_tasks)
  31.         
  32.     def __iter__(self):
  33.         return self.parser
  34.         
  35. class _ProgressWrapper(object):
  36.     """ helper-class to fake the behavoiur of the progress_hook for
  37.     the connections object.
  38.     """
  39.     def __init__(self, hook, blocksize):
  40.         self.counter = 0
  41.         self.ticks = 0
  42.         self.hook = hook
  43.         self.blocksize = blocksize
  44.         
  45.     def reset(self):
  46.         self.counter = 0
  47.         self.ticks = 0
  48.         
  49.     def __call__(self, size, blocksize=None):
  50.         self.counter += 1
  51.         if not self.counter % self.blocksize or blocksize is None:
  52.             self.ticks += 1
  53.             self.hook(self.ticks, self.blocksize, size)
  54.  
  55. class LPBugList(set):
  56.     """ set-like representation of a launchpad query
  57.     
  58.     TODO:
  59.         * on-demand parsing -thekorn: DONE, we are using generators now, that should be enough
  60.         * documentation
  61.     """
  62.     @staticmethod
  63.     def _create_progress_hook(hook_func, blocksize=25):
  64.         assert blocksize, "blocksize needs to be an integer greater than 0"
  65.         assert callable(hook_func), "hook_func needs to be callable with three arguments"
  66.         return _ProgressWrapper(hook_func, blocksize)
  67.     
  68.     def __new__(cls, *args, **kwargs):
  69.         """ this is necessary to allow keyword-arguments in python2.4 """
  70.         obj = set.__new__(cls)
  71.         return obj
  72.     
  73.     def __init__(self, baseurl, connection=None, all_tasks=False,
  74.                     helper_bugpage=None, progress_hook=None):
  75.                         
  76.         ##why?->
  77.         self.__args = locals().copy()
  78.         del self.__args["self"]
  79.         ###<-
  80.         assert connection, "Connection object needed"
  81.         self.__connection = connection
  82.         self.__all_tasks = all_tasks
  83.         
  84.         self.set_progress_hook(progress_hook)
  85.         
  86.         if hasattr(baseurl, "baseurl") and hasattr(baseurl, "functions"):
  87.             self.baseurl = baseurl.baseurl
  88.             self.__filter = baseurl.functions
  89.         else:
  90.             self.baseurl = baseurl
  91.             self.__filter = set()
  92.         
  93.         self.class_helper_bugpage = helper_bugpage
  94.             
  95.         set.__init__(self)
  96.         self._add(self.baseurl)
  97.         
  98.     def __repr__(self):
  99.         return "<BugList %s>" %self.baseurl.split("?")[0]
  100.         
  101.     def __str__(self):
  102.         return "BugList([%s])" %",".join(repr(i) for i in self)
  103.         
  104.     def __copy__(self):
  105.         return self.__class__(**self.__args)    
  106.         
  107.     def copy(self):
  108.         return copy.copy(self)
  109.     
  110.     def __iadd__(self, other):
  111.         """ let l be an instance of BugList,
  112.         l += BugList(<LP-URL>) will add bugs to l
  113.         l.add(<LP-URL>) is still possible
  114.         """
  115.         for bug in other:
  116.             self.add(bug)
  117.         return self
  118.     
  119.     def sort(self, optsort):
  120.         """ returns a LIST of bugs sorted by optsort """
  121.         raise NotImplementedError
  122.         
  123.     def _fetch(self, url):
  124.         """
  125.         searches given bugpage for bugs and 'next'-page and returns both values
  126.         
  127.         if calling <url> returns an LaunchpadError, this error will be
  128.         ignored if there are still bugs in the buglist, otherwise raised
  129.         again
  130.         """
  131.         if self.class_helper_bugpage is None:
  132.             raise NotImplementedError, 'this method must be implemented by a concrete subclass'
  133.         try:
  134.             bugpage = self.class_helper_bugpage(url,
  135.                 connection=self.__connection, all_tasks=self.__all_tasks)
  136.         except LaunchpadError:
  137.             bugpage = None
  138.             if not self:
  139.                 raise
  140.         return bugpage    
  141.  
  142.     def _add(self, url, follow=True):
  143.         """ adds bugs to the buglist """
  144.         follow_page = True
  145.         bugpage = None
  146.         if not self.__progress_hook is None:
  147.             # reset progress-hook incase there are more than one 
  148.             # fetching actions for one LPBugList-object
  149.             self.__progress_hook.reset()
  150.         while(url and follow_page):
  151.             bugpage = self._fetch(url)
  152.             if bugpage is None:
  153.                 break
  154.             if self.__filter:
  155.                 #has filter-funct
  156.                 b = self._stoppable_filter(bugpage)
  157.             else:
  158.                 if self.__progress_hook is None:
  159.                     b = bugpage
  160.                 else:
  161.                     b = itertools.imap(lambda x: self.__progress_hook(bugpage.length, bugpage.batchsize) or x, bugpage)
  162.             self += b
  163.             url = bugpage.following_page
  164.             follow_page = follow and bool(url)
  165.         if bugpage is not None and self.__progress_hook is not None:
  166.             self.__progress_hook(bugpage.length)
  167.             
  168.     def _stoppable_filter(self, bugpage):
  169.         for bug in bugpage:
  170.             if not self.__progress_hook is None:
  171.                 self.__progress_hook(bugpage.length, bugpage.batchsize)
  172.             try:
  173.                 for func in self.__filter:
  174.                     bug = func(bug)
  175.                     if not bug:
  176.                         break
  177.                 else:
  178.                     yield bug
  179.             except StopIteration:
  180.                 bugpage.following_page = False
  181.                 break
  182.             
  183.     def _get_class_helper_bugpage(self):
  184.         if not self.__class_helper_bugpage:
  185.             raise NotImplementedError, 'this method must be implemented by a concrete subclass'
  186.         return self.__class_helper_bugpage
  187.         
  188.     def _set_class_helper_bugpage(self, value):
  189.         #if not (issubclass(value, LPBugPage) or value is None):
  190.         if not (value is None or issubclass(value, LPBugPage)):
  191.             raise TypeError, "BugList.class_helper_bugpage needs to be instance of LPBugPage"
  192.         self.__class_helper_bugpage = value
  193.     class_helper_bugpage = property(_get_class_helper_bugpage, _set_class_helper_bugpage)
  194.             
  195.     def get_bugs(self):
  196.         # should be removed in the future, as self is now a set itself
  197.         return self
  198.     bugs = property(fget=get_bugs,doc="get list of bugs")
  199.         
  200.     def set_progress_hook(self, hook_func, blocksize=25):
  201.         if hook_func is None:
  202.             self.__progress_hook = None
  203.         elif isinstance(hook_func, _ProgressWrapper):
  204.             self.__progress_hook = hook_func
  205.         else:
  206.             self.__progress_hook = self._create_progress_hook(hook_func, blocksize)
  207.